/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2011 SMUNIX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*
* Author: Providence M. Salumu <providence.salumu@smunix.com>
*/

#include "test.hpp"
#include <slib/aggregator.hpp>
#include <slib/singleton.hpp>
#include <slib/frame.hpp>

#define LOG_TEST() BOOST_TEST_MESSAGE ("line=" << __LINE__ << ", func=" <<BOOST_CURRENT_FUNCTION)

#define IFACE(x) \
struct I ## x \
{ \
  virtual ~I ## x (void) {} \
  virtual int CallFrom ## I ##  x (void) { return 0; } \
};

IFACE(A);
IFACE(B);
IFACE(C);
IFACE(D);

int ConstructedN = 0;

template<class X>
struct Visitor : public smunix::Singleton<Visitor<X> >
{
  void
    Visit(void)
  {
    ++_;
  }
  int
    VisitedN(void) const
  {
    return _;
  }
  void
    Reset(void)
  {
    _ = 0;
  }
  int _ = 0;
};

template<class X>
struct HowManyTimeIsConstructed : public smunix::Singleton<HowManyTimeIsConstructed<X> >
{
  void ConstructorCalled (void)
  {
    ++_count;
  }
  int operator() (void) const
  {
    return _count;
  }
  void Reset (void)
  {
    _count = 0;
  }
  int _count = 0;
};

struct A_ : public IA, smunix::Aggregator<A_, IB, IC>
{
  typedef A_ ThisType;
  A_()
  {
    ++ConstructedN;
  }
  virtual int
    CallFromIA(void)
  {
    return 1 + Ref<IB>().CallFromIB() + Ref<IC>().CallFromIC();
  }
  template<class X>
  void
    VisitAndInject(X &x)
  {
    typedef Visitor<ThisType> VisitorTp;
    VisitorTp::Instance().Visit();
    smunix::Aggregator<A_, IB, IC>::template VisitAndInject(x);
  }
};

struct B_ : public IB, smunix::Aggregator<B_, IC>
{
  typedef B_ ThisType;
  B_()
  {
    HowManyTimeIsConstructed<ThisType>::Instance().ConstructorCalled ();
    ++ConstructedN;
  }
  virtual ~B_ (void)
  {
    HowManyTimeIsConstructed<ThisType>::Instance().Reset ();
  }
  virtual int
    CallFromIB(void)
  {
    return 2 + Ref<IC>().CallFromIC();
  }
  template<class X>
  void
    VisitAndInject(X &x)
  {
    typedef Visitor<ThisType> VisitorTp;
    VisitorTp::Instance().Visit();
    smunix::Aggregator<B_, IC>::template VisitAndInject(x);
  }
};

struct C_ : public IC
{
  typedef C_ ThisType;
  C_()
  {
    ++ConstructedN;
  }
  virtual int
    CallFromIC(void)
  {
    return _;
  }
  template<class X>
  void
    VisitAndInject(X &)
  {
    typedef Visitor<ThisType> VisitorTp;
    VisitorTp::Instance().Visit();
  }
  void
    Reset(int v = 0)
  {
    _ = v;
  }
  int _ = 3;
};

struct D_ : public ID, smunix::Aggregator<D_, C_>
{
  typedef D_ ThisType;
  D_()
  {
    ++ConstructedN;
  }
  virtual int
    CallFromID(void)
  {
    return _;
  }
  void
    Reset(int v = 0)
  {
    _ = v;
  }
  int _ = 4;
};

TEST_NAMESPACE_BEGIN ()
  struct Fixture
  {
    Fixture(void)
    {
    }
    virtual
      ~Fixture(void)
    {
    }
    typedef smunix::Frame<A_, B_, C_, D_>::ThisType TestFrame;
    TestFrame frame;
  };

  BOOST_FIXTURE_TEST_SUITE (, Fixture)

    BOOST_AUTO_TEST_CASE (ConstructAllTypes)
  {
    frame.Ref<A_>();
    frame.Ref<B_>();
    frame.Ref<C_>();
    frame.Ref<D_>();
    BOOST_CHECK_EQUAL(4, ConstructedN);
  }

  BOOST_AUTO_TEST_CASE (IsBaseOfAggregatedBase)
  {
    {
      auto v =
        typename std::is_base_of<smunix::details::AggregatorBase, A_>::type();
      BOOST_CHECK(v);
    }
    {
      auto v =
        typename std::is_base_of<smunix::details::AggregatorBase, B_>::type();
      BOOST_CHECK(v);
    }
    {
      auto v =
        typename std::is_base_of<smunix::details::AggregatorBase, C_>::type();
      BOOST_CHECK_NE(true, v);
    }
    {
      auto v =
        typename std::is_base_of<smunix::details::AggregatorBase, D_>::type();
      BOOST_CHECK(v);
    }
  }

  BOOST_AUTO_TEST_CASE (VisitNotToBeCalledOnNonAggregator)
  {
    Visitor<D_>::Instance().Reset();
    Visitor<C_>::Instance().Reset();
    Visitor<B_>::Instance().Reset();
    Visitor<A_>::Instance().Reset();

    TestFrame t;

    BOOST_CHECK_EQUAL(0, Visitor<D_>::Instance ().VisitedN ());
    BOOST_CHECK_EQUAL(0, Visitor<C_>::Instance ().VisitedN ());
    BOOST_CHECK_EQUAL(3, Visitor<B_>::Instance ().VisitedN ());
    BOOST_CHECK_EQUAL(3, Visitor<A_>::Instance ().VisitedN ());
  }

  BOOST_AUTO_TEST_CASE (InjectionIsCompletelyDone)
  {
    BOOST_CHECK_EQUAL(3, frame.Ref<D_> ().Ref<C_> ().CallFromIC ());
    BOOST_CHECK_EQUAL(4, frame.Ref<D_> ().CallFromID ());
    BOOST_CHECK_EQUAL(3, frame.Ref<C_> ().CallFromIC ());
    BOOST_CHECK_EQUAL(9, frame.Ref<A_> ().CallFromIA ());
    BOOST_CHECK_EQUAL(5, frame.Ref<B_> ().CallFromIB ());
    frame.Ref<D_>().Ref<C_>().Reset(300);
    frame.Ref<D_>().Reset(400);
    BOOST_CHECK_EQUAL(300, frame.Ref<D_> ().Ref<C_> ().CallFromIC ());
    BOOST_CHECK_EQUAL(400, frame.Ref<D_> ().CallFromID ());
    BOOST_CHECK_EQUAL(300, frame.Ref<C_> ().CallFromIC ());
  }
  BOOST_AUTO_TEST_SUITE_END()
    // Fixture
  TEST_NAMESPACE_END ()
  TEST_NAMESPACE_BEGIN ()
  struct DuplicatedTypesFixture
  {
    DuplicatedTypesFixture(void)
    {
    }
    virtual
      ~DuplicatedTypesFixture(void)
    {
    }
    typedef smunix::Frame<A_, B_, C_, D_, B_>::ThisType TestFrame;
    TestFrame frame;
  };

  BOOST_FIXTURE_TEST_SUITE (, DuplicatedTypesFixture)

    BOOST_AUTO_TEST_CASE (DuplicatedTypesInFrame)
  {
    BOOST_CHECK_EQUAL(5, frame.Ref<B_> ().CallFromIB ());
  }
  BOOST_AUTO_TEST_CASE (ConstructorCalledOnlyOnce)
  {
    BOOST_CHECK_EQUAL (1, HowManyTimeIsConstructed<B_>::Instance() ());
  }
  BOOST_AUTO_TEST_SUITE_END ()
    // DuplicatedTypesFixture
  TEST_NAMESPACE_END ()
